package cn.slipi.core.plugin.routing;

import cn.slipi.common.constants.Constants;
import cn.slipi.common.entity.PdrApi;
import cn.slipi.common.entity.RequestLog;
import cn.slipi.common.enums.PluginDetailEnum;
import cn.slipi.common.exception.InternalException;
import cn.slipi.common.rule.custom.entity.*;
import cn.slipi.core.cache.ApiManager;
import cn.slipi.core.component.http.WebClientRequest;
import cn.slipi.core.component.loadbalance.LoadBalanceFactory;
import cn.slipi.core.component.loadbalance.LoadBalanceSelector;
import cn.slipi.core.plugin.AbstractPlugin;
import cn.slipi.core.plugin.IPluginChain;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Joiner;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * HttpPlugin
 */
public class HttpPlugin extends AbstractPlugin {

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpPlugin.class);

    private final ApiManager apiManager;

    public HttpPlugin(ApiManager apiManager) {
        super(apiManager);
        this.apiManager = apiManager;
    }

    @Override
    protected Mono<Void> doExecute(ServerWebExchange exchange, IPluginChain chain) {
        RequestLog requestLog = (RequestLog) exchange.getAttributes().get(Constants.REQUEST_LOG);

        MatchCondition requestCondition = (MatchCondition) exchange.getAttributes().get(Constants.GATEWAY_HEADER);

        PdrApi pdrApi = apiManager.getApiByCode(requestCondition.getApiCode());

        if (pdrApi == null) {
            return this.response(HttpStatus.NOT_FOUND);
        } else if (pdrApi.getEnable().equals(0)) {
            return this.response(HttpStatus.NOT_FOUND);
        } else if (!pdrApi.getQueryType().equalsIgnoreCase(requestCondition.getQueryType())) {
            return this.response(HttpStatus.METHOD_NOT_ALLOWED);
        }

        LoadBalance loadBalance = JSONObject.parseObject(pdrApi.getLoadBalance(), LoadBalance.class);

//        HystrixParam hystrixParam = JSONObject.parseObject(pdrApi.getHystrixParam(), HystrixParam.class);

        LoadBalanceSelector loadBalanceSelector = LoadBalanceFactory.of(loadBalance.getStrategy());
        BalancePiece selectBalancePiece = loadBalanceSelector.select(loadBalance, pdrApi.getLoadIp());

        String server = (String) exchange.getAttributes().get(Constants.GRAY_RELEASE_LOAD_BALANCE);
        String requestUrl;
        if (StringUtils.isNotBlank(server)) {
            requestUrl = buildUrl(exchange, server, pdrApi.getPath());
        } else {
            requestUrl = buildUrl(exchange, selectBalancePiece.getServer(), pdrApi.getPath());
        }

        WebClientRequest request = new WebClientRequest(
                exchange,
                chain,
                requestCondition,
                requestUrl,
                loadBalance.getRetryTime()
        );

        requestLog.setHttp("ok");
        exchange.getAttributes().put(Constants.REQUEST_LOG, requestLog);

        return request.invoke().doOnError(this::dealError).then();

    }

    /**
     * 转发请求异常处理
     *
     * @param e
     */
    private void dealError(Throwable e) {
        throw new InternalException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ((InternalException) e).getMsg());
    }

    private String buildUrl(ServerWebExchange exchange, String server, String path) {
        server = StringUtils.trim(server);
        if (!server.contains("http")) {
            server = "http://" + server;
        }
        if (path.startsWith("/")) {
            path = path.substring(1);
        }
        String preUrl = Joiner.on("/").join(server, path);
        return preUrl + urlParams(exchange);
    }

    private String urlParams(ServerWebExchange exchange) {
        String pathParams = exchange.getRequest().getPath().toString();
        MultiValueMap queryParams = exchange.getRequest().getQueryParams();
        //判断参数是path还是query
        if (StringUtils.isNotBlank(pathParams) && !pathParams.equals("/")) {
            return pathParams;
        } else if (!queryParams.isEmpty()) {
            return generatorQueryParams(queryParams);
        } else {
            return "";
        }
    }

    private String generatorQueryParams(MultiValueMap queryParams) {
        Map<String, String> params = new HashMap<>();
        queryParams.toSingleValueMap().forEach((key, value) -> params.put(key.toString(), value.toString()));
        String paramsStr = params.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).collect(Collectors.joining("&"));
        return String.format("%s%s", "?", paramsStr);
    }


    @Override
    public String getPluginPhase() {
        return PluginDetailEnum.HTTP.getPhase();
    }

    @Override
    public int getCode() {
        return PluginDetailEnum.HTTP.getCode();
    }

    @Override
    public String getName() {
        return PluginDetailEnum.HTTP.getName();
    }
}
